﻿using System.Collections.Generic;
using Mono.Cecil.Cil;

namespace YAPL.Language.Compiler.ILOptimization.Rewriters {
	public class ReplacementRewriter : IRewriter {
		private readonly List<List<OpCode>> _sequences = new List<List<OpCode>>();
		private readonly List<List<OpCode>> _replacements = new List<List<OpCode>>();
		public void Rewrite(MethodBody body) {
			var currentInstruction = 0;
			while (currentInstruction < body.Instructions.Count) {
				for (int i = 0; i < _sequences.Count; i++) {
					if (SequenceMatch(body, currentInstruction, i)) {
						ReplaceSequence(body, currentInstruction, i);
					}
				}
				currentInstruction++;
			}
		}

		private bool SequenceMatch(MethodBody body, int currentInstruction, int sequenceIndex) {
			var instructions = body.Instructions;
			if (currentInstruction + _sequences[sequenceIndex].Count < instructions.Count) {
				for (var i = 0; i < _sequences[sequenceIndex].Count; i++) {
					if (_sequences[sequenceIndex][i] != instructions[currentInstruction + i].OpCode)
						return false;
				}
				return true;
			}
			return false;
		}

		private void ReplaceSequence(MethodBody body, int currentInstruction, int replacementIndex) {
			var instructions = body.Instructions;
			for (var i = 0; i < _sequences[replacementIndex].Count; i++) {
				instructions.RemoveAt(currentInstruction);
			}

			var processor = body.CilWorker;
			for (var i = 0; i < _replacements[replacementIndex].Count; i++) {
				var replacement = processor.Create(_replacements[replacementIndex][i]);
				processor.InsertBefore(instructions[currentInstruction+i], replacement);
			}
		}

		public void AddReplacement(List<OpCode> sequence, List<OpCode> replacement) {
			_sequences.Add(sequence);
			_replacements.Add(replacement);
		}
	}
}
